package be.gnx.log4jeclipse.log4j;

import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.spi.LoggingEvent;

import be.gnx.log4jeclipse.log4j.Log4JListenerCallbacks.PortBacklogInetaddress;

public class Log4JListener implements Runnable {

  private static final Log4JListener log4JListener = new Log4JListener();

  private ServerSocket serverSocket;

  protected final List<Log4JListenerEvents> log4JListenerEventsList = new ArrayList<Log4JListenerEvents>();

  protected final List<Log4JListenerCallbacks> log4JListenerCallbacksList = new ArrayList<Log4JListenerCallbacks>();

  private Thread thread;

  private Log4JListener() {
    super();
  }

  public static Log4JListener getInstance() {
    return log4JListener;
  }

  public void addLog4JListenerCallBacks(
      Log4JListenerCallbacks log4JListenerCallbacks) {
    log4JListenerCallbacksList.add(log4JListenerCallbacks);
  }

  public void addLog4JListenerEvents(Log4JListenerEvents log4JListenerEvents) {
    log4JListenerEventsList.add(log4JListenerEvents);
  }

  public void run() {
    List<PortBacklogInetaddress> portBacklogInetaddressList = getPortBacklogInetaddressList();
    if (portBacklogInetaddressList.size() > 0) {
      // TODO listen on more than one inetAddress and port
      try {
        int port = portBacklogInetaddressList.get(0).port;
        int backlog = portBacklogInetaddressList.get(0).backlog;
        InetAddress inetAddress = portBacklogInetaddressList.get(0).inetAddress;
        serverSocket = new ServerSocket(port, backlog, inetAddress);
        serverSocket.setReuseAddress(true);
        while (true) {
          final Socket socket = serverSocket.accept();
          final Thread thread = new Thread(new AcceptedSocket(socket, this));
          thread.setDaemon(true);
          thread.start();
        }
      } catch (IOException ioException) {
        System.err.println(ioException);
      }
    } else {
      System.err.println("server not started : no port specified");
    }
  }

  public boolean isClosed() {
    return serverSocket == null || serverSocket.isClosed();
  }

  protected List<PortBacklogInetaddress> getPortBacklogInetaddressList() {
    List<PortBacklogInetaddress> portBacklogInetaddressList = new ArrayList<PortBacklogInetaddress>();
    for (Log4JListenerCallbacks log4JListenerCallbacks : log4JListenerCallbacksList) {
      portBacklogInetaddressList.addAll(log4JListenerCallbacks
          .getPortBacklogInetaddressList());
    }
    return portBacklogInetaddressList;
  }

  protected void receivedLoggingEvent(LoggingEvent loggingEvent, String client) {
    for (Log4JListenerEvents log4JListenerEvents : log4JListener.log4JListenerEventsList) {
      log4JListenerEvents.receivedLoggingEvent(loggingEvent, client);
    }
  }

  public void start(boolean daemon) {
    thread = new Thread(log4JListener);
    thread.setDaemon(daemon);
    thread.start();
  }

  public void stop() {
    thread.interrupt();
  }

}
